﻿using Microsoft.AspNetCore.Authorization;
using RndMaterialDisposalService.Attrbutes;
using System.Security.Claims;

namespace RndMaterialDisposal.Extensions.Authorization
{
    [NAutowired(Lifecycle.Singleton)]
    public class PermissionAuthorizationHandler: AuthorizationHandler<PermissionAuthorizationRequirement>
    {
        /// <summary>
        /// Makes a decision if authorization is allowed based on a specific requirement.
        /// 针对某个requirement做授权。
        /// 如果实现了HandleAsync方法，该方法不走。
        /// </summary>
        /// <param name="context"></param>
        /// <param name="requirement"></param>
        /// <returns></returns>
        protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, PermissionAuthorizationRequirement requirement)
        {
            var permissions = context.User.Claims.Where(x => x.Type == "Permission").Select(x => x.Value).ToList();
            if (permissions.Any(_ => _.StartsWith(requirement.Name)))
            {
                context.Succeed(requirement);
            }
            return Task.CompletedTask;
        }
        /// <summary>
        /// Makes a decision if authorization is allowed.
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public override Task HandleAsync(AuthorizationHandlerContext context)
        {
            // 当前访问 Controller/Action 所需要的权限(策略授权)
            IAuthorizationRequirement[] pendingRequirements = context.PendingRequirements.ToArray();
            // 取出用户信息
            IEnumerable<Claim> claims = context.User?.Claims;

            // 未登录或者取不到用户信息
            if (claims is null)
            {
                context.Fail();
                return Task.CompletedTask;
            }

            // 取出用户名
            Claim userName = claims.FirstOrDefault(x => x.Type == ClaimTypes.Name);
            if (userName is null)
            {
                context.Fail();
                return Task.CompletedTask;
            }
            // ... 省略一些检验过程 ...

            // 获取此用户的信息，实际动态获取，用户权限最好缓存起来，每次调用接口很浪费性能。
            User user = UsersData.Users.FirstOrDefault(x => x.Name.Equals(userName.Value, StringComparison.OrdinalIgnoreCase));
            if (user is null) {
                context.Fail();
                return Task.CompletedTask;
            }
            List<string> auths = user.Role.UserPermission;  //用户所有的权限。

            // 逐个检查
            foreach (PermissionAuthorizationRequirement requirement in pendingRequirements)
            {
                // 如果用户权限列表中没有找到此权限的话
                if (!auths.Any(x => x == requirement.Name))
                {
                    context.Fail();
                    return Task.CompletedTask;
                }

                context.Succeed(requirement);
            }
            return Task.CompletedTask;
        }
    }
}
